module NANDFlash_Controller(
    /***************flash port****************/
    output      CE_n,
    output      WE_n,
    output      RE_n,
    output      CLE,
    output      ALE,
    inout[7:0]  DQ,
    inout       DQS,
    input       RorB_n,
    output      WP,
    /**************mode control port***********/
    input       clk,
    input       rst_n,
    input[39:0] add,
    inout[7:0]  dataInput,
    output      DI_en,
    output      dataOutPut,
    output      DO_en,
    input       commandInput,
    input       start,
    output      CycleFinish  
);

parameter portMode = 3'h001;

parameter CMD_RESET =                 8'hFF;
parameter CMD_READ_ID =               8'h90;
parameter CMD_READ_STATUS =           8'h70;
parameter CMD_READ_PAGE_C0 =          8'h00;
parameter CMD_READ_PAGE_C1 =          8'h30;
parameter CMD_READ_PAGE_CACHE =       8'h31;
parameter CMD_READ_PAGE_CACHE_CASE =  8'h3f;
parameter CMD_READ_MUL_C0 =           8'h00;
parameter CMD_READ_MUL_C1 =           8'h32;
parameter CMD_PROG_PAGE_C0 =          8'h80;
parameter CMD_PROG_PAGE_C1 =          8'h10;
parameter CMD_PROG_PAGE_CACHE_C0 =    8'h80;
parameter CMD_PROG_PAGE_CACHE_C1 =    8'h15;
parameter CMD_PROG_PAGE_MUL_C0 =      8'h80;
parameter CMD_PROG_PAGE_MUL_C1 =      8'h11;
parameter CMD_ERASE_BLOCK_CO =        8'h60;
parameter CMD_ERASE_BLOCK_C1 =        8'hD0;
parameter CMD_ERASE_BLOCK_MUL_C0 =    8'h60;
parameter CMD_ERASE_BLOCK_MUL_C1 =    8'hD1;

parameter State_command =   3'b000;
parameter State_adderss =   3'b001;
parameter State_WriteData = 3'b010;
parameter State_ReadData  = 3'b011;
parameter State_Idle =      3'b100;
parameter State_WriteWait = 3'b111;


parameter PAGE_SIZE = 32'h10;

reg[2:0]  port_cmd_reg;
reg[7:0]  port_data_in_reg;
wire      port_data_in_en;
wire[7:0] port_data_out_reg;
wire      port_data_out_vaild;

wire[31:0] port_data_number;
wire[31:0] port_data_counter;

reg[31:0] read_data_number;
reg[31:0] read_data_counter;

reg[31:0] write_data_number;
reg[31:0] write_data_counter;

wire we_s_w;
wire re_s_w;
wire cle_w;
wire ale_w;
wire[7:0] dq_w;
wire dqs_w;
wire rb_n_w;
wire rb_s_w;
wire wp_s_w;

assign WE_n = we_s_w;
assign RE_n = re_s_w;
assign CLE = cle_w;
assign ALE = ale_w;
assign DQ = dq_w;
assign DQS = dqs_w;
assign RorB_n = rb_n_w;
assign WP = wp_s_w;

reg    rb_s_r;
assign rb_s_w = rb_s_r;

reg[39:0] address;

/*********************** toggle mode port **************************/
Toggle_port u_Toggle_port(
	.WE_s          (we_s_w      ),
    .RE_s          (re_s_w      ),
    .CLE           (cle_w       ),
    .ALE           (ale_w       ),
    .DQ            (dq_w        ),
    .DQS           (dqs_w       ),
    .RB            (rb_s_w      ),
   // .WP_s          (wp_s_w      ),

    .clkX2         (clk                 ),
    .rst_n         (rst_n               ),
    .en            (portMode[0]         ),
    .Cmd           (port_cmd_reg        ),
    .InputVal      (port_data_in_reg    ),
    .IV_En         (port_data_in_en     ),
    .ReadVal       (port_data_out_reg   ),
    .RV_En         (port_data_out_vaild ),
    .RWDataNum     (port_data_number    ),
    .RWDataCounter (port_data_counter   ),
    .RB_n          (rb_n_w              )
);

/*********************** async mode cycle **************************/
Async_port u_Async_port(
	.WE_s     (we_s_w     ),
    .RE_s     (re_s_w     ),
    .CLE      (cle_w      ),
    .ALE      (ale_w      ),
    .DQ       (dq_w       ),
    .RB       (rb_s_w     ),
    .WP_s     (wp_s_w     ),

    .clk      (clk                  ),
    .rst_n    (rst_n                ),
    .isEDO    (1'b0                 ),
    .en       (portMode[1]          ),
    .Cmd      (port_cmd_reg         ),
    .InputVal (port_data_in_reg     ),
    .IV_en    (port_data_in_en      ),
    .ReadVal  (port_data_out_reg    ),
    .RV_En    (port_data_out_vaild  ),
    .RB_n     (rb_n_w               )
);

/*********************** sync mode cycle **************************/
Sync_port u_Sync_port(
	.CLK           (we_s_w      ),
    .WR            (re_s_w      ),
    .CLE           (cle_w       ),
    .ALE           (ale_w       ),
    .DQ            (dq_w        ),
    .DQS           (dqs_w       ),
    .RB            (rb_s_w      ),
    .WP_s          (wp_s_w      ),

    .clkX2         (clk                 ),
    .rst_n         (rst_n               ),
    .en            (portMode[2]         ),
    .Cmd           (port_cmd_reg        ),
    .InputVal      (port_data_in_reg    ),
    .IV_En         (port_data_in_en     ),
    .ReadVal       (port_data_out_reg   ),
    .RV_En         (port_data_out_vaild ),
    .RWDataNum     (port_data_number    ),
    .RWDataCounter (port_data_counter   ),
    .RB_n          (rb_n_w              )
);

localparam RESET =       6'b000001;
localparam READ_PAGE =   6'b000010;
localparam WRITE_PAGE =  6'b000100;
localparam READ_ID =     6'b001000;  
localparam ERASE_BLOCK = 6'b010000;
localparam IDLE =        6'b100000;

reg[5:0] cur_state;

initial begin
    cur_state = IDLE;
    #100  cur_state = READ_PAGE;
    #400  cur_state = IDLE;
    #50   cur_state = WRITE_PAGE;
end

reg      address_cycle;
reg      command_cycle;
reg      read_data_cycle;
reg      write_data_cycle;
reg      read_page_finish;//读页周期完成
reg      write_page_finish;//写页周期完成

reg[7:0] data_r_a;
reg[7:0] data_r_c;
reg[7:0] data_r_rd;
reg[7:0] data_r_wd;

reg[7:0] cmd_r_a;
reg[7:0] cmd_r_c;
reg[7:0] cmd_r_rd;
reg[7:0] cmd_r_wd;

reg      add_cycle_tri_n;  //地址周期触发信号，下降沿触发
reg      add_cycle_fin_n; //地址周期完成信号，下降沿有效
reg      cmd_cycle_tri_n;
reg      cmd_cycle_fin_n;
reg      rd_cycle_tri_n;
reg      rd_cycle_fin_n;
reg      wd_cycle_tri_n;
reg      wd_cycle_fin_n;

reg[7:0] port_cmd_reg;

reg readFinish;
reg writeFinish;
reg[3:0] addressNum;


reg rd_rst_n;
reg wd_rst_n;

always @(*) begin
    if (address_cycle == 0) begin
        port_data_in_reg = data_r_a;
        port_cmd_reg = cmd_r_a;
    end else if(command_cycle == 0) begin
        port_data_in_reg = data_r_c;
        port_cmd_reg = cmd_r_c;
    end else if(read_data_cycle == 0) begin
        port_cmd_reg = cmd_r_rd;
        port_data_in_reg = data_r_rd;
    end else if(write_data_cycle == 0) begin
        port_cmd_reg = cmd_r_wd;
        port_data_in_reg = data_r_wd;
    end else begin
        port_cmd_reg <= State_Idle;
        port_data_in_reg <= 8'h00;
    end
end

assign port_data_number = cur_state == WRITE_PAGE ? write_data_number : read_data_number;
assign port_data_counter = cur_state == WRITE_PAGE ? write_data_counter : read_data_counter;

always @(*) begin
    if (cur_state == READ_PAGE) begin
        add_cycle_tri_n = rd_rst_n;
        if (readFinish) begin
            port_cmd_reg = CMD_READ_PAGE_C1;
            cmd_cycle_tri_n = rd_cycle_fin_n;
        end else begin
            port_cmd_reg = CMD_READ_PAGE_C0;
            cmd_cycle_tri_n = add_cycle_fin_n;
            rd_cycle_tri_n = cmd_cycle_fin_n;
        end
    end else if(cur_state == WRITE_PAGE) begin
        add_cycle_tri_n = wd_rst_n;
        if (writeFinish) begin
            port_cmd_reg = CMD_PROG_PAGE_C1;
            cmd_cycle_tri_n = wd_cycle_fin_n;
        end else begin
            port_cmd_reg = CMD_PROG_PAGE_C0;
            cmd_cycle_tri_n = add_cycle_fin_n;
            wd_cycle_tri_n = cmd_cycle_fin_n;
        end
    end else begin
        add_cycle_tri_n = 1'b1;
    end
end

/*********************** read page **************************/

always @(posedge read_data_cycle or negedge rd_rst_n) begin
    if (rd_rst_n & cur_state == READ_PAGE) begin
        readFinish <= 1'b1;
    end else begin
        readFinish <= 1'b0;
        addressNum <= 4'h5;
    end
end

always @(posedge cmd_cycle_fin_n or negedge rd_rst_n)begin
    if (rd_rst_n & cur_state == READ_PAGE & readFinish) begin
        read_page_finish <= 1'b1;
    end else begin
        read_page_finish <= 1'b0;
    end
end

reg[1:0] DRtriCounter;
always @(posedge clk or negedge rst_n) begin
    if (rst_n) begin
        if (cur_state == READ_PAGE) begin
            if (DRtriCounter < 2'b10) begin
                rd_rst_n <= 1'b0;
                DRtriCounter <= DRtriCounter + 1'b1;
            end else begin
                rd_rst_n <= 1'b1;
                DRtriCounter <= DRtriCounter;
            end
        end else begin
            DRtriCounter <= 2'b00;
            rd_rst_n <= 1'b1;
        end
    end else begin
        rd_rst_n <= 1'b1;
        DRtriCounter <= 2'b11;
    end
end

/*********************write page ***************************/

always @(posedge write_data_cycle or negedge wd_rst_n) begin
    if (wd_rst_n & cur_state == WRITE_PAGE) begin
        writeFinish <= 1'b1;
    end else begin
        writeFinish <= 1'b0;
        addressNum <= 4'h5;
    end
end

always @(posedge cmd_cycle_fin_n or negedge wd_rst_n)begin
    if (wd_rst_n & cur_state == WRITE_PAGE & writeFinish) begin
        write_page_finish <= 1'b1;
    end else begin
        write_page_finish <= 1'b0;
    end
end

reg[1:0] DWtriCounter;
always @(posedge clk or negedge rst_n) begin
    if (rst_n) begin
        if (cur_state == WRITE_PAGE) begin
            if (DWtriCounter < 2'b10) begin
                wd_rst_n <= 1'b0;
                DWtriCounter <= DWtriCounter + 1'b1;
            end else begin
                wd_rst_n <= 1'b1;
                DWtriCounter <= DWtriCounter;
            end
        end else begin
            DWtriCounter <= 2'b00;
            wd_rst_n <= 1'b1;
        end
    end else begin
        wd_rst_n <= 1'b1;
        DWtriCounter <= 2'b11;
    end
end


/****************************************************************/

/*********************** address cycle **************************/
reg[3:0] addCounter;
always @(negedge clk or negedge add_cycle_tri_n or negedge rst_n) begin
    if (rst_n) begin
        if (add_cycle_tri_n == 1'b1) begin
            if (address_cycle == 1'b0 & addCounter < addressNum) begin
                cmd_r_a <= State_adderss;
                if (port_data_in_en & rb_n_w) begin
                    data_r_a <= address[7:0];
                    address <= address >> 8;
                    addCounter <= addCounter + 1'b1;
                end else begin
                    data_r_a <= data_r_a;
                    address <= address;
                    addCounter <= addCounter;                    
                end 
            end else begin
                address_cycle <= 1'b1;
                addCounter <= 4'h0;
                cmd_r_a <= State_Idle;
            end
        end else if(add_cycle_tri_n == 1'b0) begin
            address_cycle <= 1'b0;
            addCounter <= 4'h0;
        end else begin
            address_cycle <= 1'b1;
            addCounter <= 4'h0;
        end
    end else begin
        address_cycle <= 1'b1;
        addCounter <= 4'h0;
     //   address <= 40'h12_34_56_78_9A;
    end
end

always @(posedge clk or posedge address_cycle or negedge rst_n) begin
    if (rst_n & clk == 1'b0 & address_cycle == 1'b1) begin
        add_cycle_fin_n <= 1'b0;
    end else begin
        add_cycle_fin_n <= 1'b1;
    end
end

/*********************** command cycle **************************/
reg cmdState;
always @(negedge clk or negedge cmd_cycle_tri_n or negedge rst_n) begin
    if (rst_n) begin
        if (cmd_cycle_tri_n == 1'b1) begin
            if (command_cycle == 1'b0 & cmdState == 1'b0) begin
               cmd_r_c <= State_command; 
               if (port_data_in_en & rb_n_w) begin
                    data_r_c <= port_cmd_reg;
                    cmdState = 1'b1;
                end else begin
                    data_r_c <= data_r_c;                     
                end
            end else begin
                command_cycle <= 1'b1;
                cmd_r_c <= State_Idle;
                data_r_c <= data_r_c;
            end
        end else if(cmd_cycle_tri_n == 1'b0) begin
            command_cycle <= 1'b0;
            cmdState <= 1'b0;
        end else begin
            command_cycle <= 1'b1;
            cmdState <= 1'b0;
        end
    end else begin
        command_cycle <= 1'b1;
        cmdState <= 1'b0;
        port_cmd_reg <= 8'hc1;
    end
end

always @(posedge clk or posedge command_cycle or negedge rst_n) begin
    if (rst_n & clk == 1'b0 & command_cycle == 1'b1) begin
        cmd_cycle_fin_n <= 1'b0;
    end else begin
        cmd_cycle_fin_n <= 1'b1;
    end
end

/*********************** read data cycle **************************/
reg[7:0] readed;
always @(negedge clk or negedge rd_cycle_tri_n or negedge rst_n) begin
    if (rst_n) begin
        if (rd_cycle_tri_n == 1'b1) begin
            if (read_data_counter < read_data_number & read_data_cycle == 1'b0) begin
                cmd_r_rd <= State_ReadData;
                if (port_data_out_vaild & rb_n_w) begin
                    readed <= port_data_out_reg;
                    read_data_counter <= read_data_counter + 1'b1;
                end else begin
                    read_data_counter <= read_data_counter;
                end
            end else begin
                read_data_cycle <= 1'b1;
                cmd_r_rd <= State_Idle;
            end
        end else if(rd_cycle_tri_n == 1'b0) begin
            read_data_cycle <= 1'b0;
            read_data_number <= PAGE_SIZE;
            cmd_r_rd <= State_Idle;
            read_data_counter <= 32'h0;
        end else begin
            read_data_cycle <= 1'b1;
            read_data_number <= PAGE_SIZE;
            cmd_r_rd <= State_Idle;
            read_data_counter <= 32'h0;
        end
    end else begin
        read_data_cycle <= 1'b1;
        read_data_number <= PAGE_SIZE;
        cmd_r_rd <= State_Idle;
        read_data_counter <= 32'h0;
    end
end

always @(posedge clk or posedge read_data_cycle or negedge rst_n) begin
    if (rst_n & clk == 1'b0 & read_data_cycle == 1'b1) begin
        rd_cycle_fin_n <= 1'b0;
    end else begin
        rd_cycle_fin_n <= 1'b1;
    end
end

/*********************** write data cycle **************************/
wire[7:0] inputData;
always @(negedge clk or negedge wd_cycle_tri_n or negedge rst_n) begin
    if (rst_n) begin
        if (wd_cycle_tri_n == 1'b1) begin
            if (write_data_cycle == 1'b0 & write_data_counter < write_data_number) begin
                cmd_r_wd <= State_WriteData;
                if (port_data_in_en & rb_n_w) begin
                    data_r_wd <= inputData;
                    write_data_counter <= write_data_counter + 1'b1;
                end else begin
                    data_r_wd <= data_r_wd;
                    write_data_counter <= write_data_counter;
                end
            end else begin
                write_data_cycle <= 1'b1;
                write_data_counter <= 32'h0;
                cmd_r_wd <= State_Idle;
            end
        end else if (wd_cycle_tri_n == 1'b0) begin
            write_data_cycle <= 1'b0;
            write_data_counter <= 32'h0;
        end else begin
            write_data_cycle <= 1'b1;
            cmd_r_wd <= State_Idle;
            write_data_number <= PAGE_SIZE;
        end
    end else begin
        write_data_cycle <= 1'b1;
        cmd_r_wd <= State_Idle;
        write_data_number <= PAGE_SIZE;
        write_data_counter <= 32'h0;
    end
end

always @(posedge clk or posedge write_data_cycle or negedge rst_n) begin
    if (rst_n & clk == 1'b0 & write_data_cycle == 1'b1) begin
        wd_cycle_fin_n <= 1'b0;
    end else begin
        wd_cycle_fin_n <= 1'b1;
    end
end

endmodule // NANDFlash_Control